#include "fxcc/core/graph/opengl3/interface.h"
#include "fxcc/core/graph/opengl3/ImGuiGlfwApp.h"


#include "fxcc/imgui/Editor.h"

using namespace fxcc::graph;
using namespace fxcc::graph::opengl3;
using namespace fxcc::imgui::graph::opengl3;
using BaseApp = fxcc::imgui::graph::opengl3::GlfwApp;

#undef main

namespace IM
{
    int id = 0;

    struct Node
    {
        bool m_Exist{ true };
        int m_Id;
        int m_InputPinId;
        int m_OutPinId;
        ImVec2 m_Pos{ 0,0 };
        bool m_FirstRender{ true };

        Node() = default;
        Node(int& index)
        {
            m_Id = index++;
            m_InputPinId = index++;
            m_OutPinId = index++;
        }
    };
    struct Link
    {
        bool m_Exist{ 1 };

        int m_Id;
        int m_OutputPinId;
        int m_InputPinId;
        bool m_CreateFromSnap;
        Link() = default;
        Link(int& index)
        {
            m_Id = index++;
            m_OutputPinId = index++;
            m_InputPinId = index++;
        }
    };

    int m_NodeIndex{ 0 };


    struct NodeEditor
    {
        std::map<int, Node> m_Nodes;
        std::map<int, Link> m_Links;

        Node& NewNode()
        {
            Node node(id);
            m_Nodes[node.m_Id] = node;
            return m_Nodes[node.m_Id];
        }

        void InstPanel()
        {
            ImNodes::BeginNodeEditor();

            for (auto& [key, value] : m_Nodes)
            {
                if (!value.m_Exist)
                {
                    continue;
                }

                ImNodes::BeginNode(value.m_Id);
                ImGui::Text("hello %d", key);
                ImNodes::BeginOutputAttribute(value.m_OutPinId);
                
                ImNodes::EndOutputAttribute();

                ImNodes::BeginInputAttribute(value.m_InputPinId);
                
                ImNodes::EndInputAttribute();
                
                ImNodes::EndNode();

                if (value.m_FirstRender)
                {
                    ztclog::info("set node %d pos %f %f", key, value.m_Pos.x, value.m_Pos.y);
                    ImNodes::SetNodeScreenSpacePos(value.m_Id, value.m_Pos);
                    value.m_FirstRender = false;
                }
            }

            for (auto& [key, value] : m_Links)
            {
                if (value.m_Exist)
                {
                    ImNodes::Link(value.m_Id, value.m_OutputPinId, value.m_InputPinId);
                }
            }

            ImGuiIO& io = ImGui::GetIO();

            if (ImGui::BeginPopupContextWindow())
            {
                if (ImGui::MenuItem("add"))
                {
                    auto& node = NewNode();
                    node.m_Pos = io.MousePos;
                }
                if (ImGui::MenuItem("Pos Fixed"))
                {
                    ImNodes::EditorContextResetPanning({ 0,0 });
                }
                ImGui::EndPopup();
            }

            ImNodes::EndNodeEditor();

            Link lk;
            bool createLink = ImNodes::IsLinkCreated(&lk.m_OutputPinId, &lk.m_InputPinId, &lk.m_CreateFromSnap);

            if (createLink)
            {
                auto& newLink = Link(id);
                newLink.m_CreateFromSnap = lk.m_CreateFromSnap;
                newLink.m_InputPinId = lk.m_InputPinId;
                newLink.m_OutputPinId = lk.m_OutputPinId;
                m_Links[newLink.m_Id] = newLink;

            }
        }
    };

};


struct SimpleApp : public BaseApp
{
public:
    fxcc::graph::common::Transform transform;

    std::shared_ptr<fxcc::graph::opengl3::RenderTargetJack> m_RenderTarget;

    std::shared_ptr<fxcc::graph::opengl3::MeshJack> m_CubeMesh;

    std::shared_ptr<fxcc::graph::opengl3::Texture2DJack> m_Texture2DJack;

    glm::vec4 color;

    std::shared_ptr<fxcc::graph::opengl3::Effect3d> m_Effect;

    fxcc::graph::common::Camera camera;

    IM::NodeEditor nodeEditor;

    SimpleApp(const fxcc::platform::common::App::Desc& desc) : BaseApp(desc), color(1.0)
    {
    }
    bool Init() override
    {
        if (!BaseApp::Init())
        {
            return false;
        }

        if (!InitResources())
        {
            return false;
        }

        return true;
    };

    fxcc::graph::common::Effect3d::BoneData m_BoneData;

    void printOpenGLESVersion()
    {
        const char* version = (const char*)glGetString(GL_VERSION);
        std::cout << "OpenGL ES Version: " << version << std::endl;
    };

    bool InitResources()
    {
        transform.m_Scale = glm::vec3(1.f);

        camera.SetPosition(glm::vec3(50.0f));
        camera.LookAt(glm::vec3(0.0f));
        camera.nNear = 1.0f;
        //camera.nFar= 2500.0f;
        const std::string fragCode = R"(

layout (location=0) out vec4 FragColor0;

uniform sampler2D diffuse;

void main()
{
	FragColor0 = texture(diffuse, TexCoord);
}
)";
        m_Effect = std::make_shared<fxcc::graph::opengl3::Effect3d>(fragCode);
        m_CubeMesh = std::make_shared<fxcc::graph::opengl3::MeshJack>(fxcc::graph::common::Geometry::CreateBox());


        return true;
    };

    fxcc::imgui::Editor editor;

    void OnEdit() override
    {
        ImGui::DockSpaceOverViewport(0, ImGui::GetMainViewport());

        ImGui::Begin("Node");

        nodeEditor.InstPanel();

        ImGui::End();
 


    }
    void OnDestroy() override
    {

        BaseApp::OnDestroy();
    }
    void OnFrameRender() override
    {
        glViewport(0, 0, m_Desc.m_Size.x, m_Desc.m_Size.y);

        glClearColor(color.x, color.y, color.z, color.w);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        m_RenderTarget->Begin();

        m_Effect->Begin();
        m_Effect->SetCamera(camera);
        m_Effect->SetObjData(transform);
        m_Effect->InstMeshJack(m_CubeMesh.get());
        m_Effect->End();

        m_RenderTarget->End();
    };

    void OnFramebuffersize(int w, int h) override
    {
        opengl3::GlfwApp::OnFramebuffersize(w, h);
        camera.SetAspect(m_Desc.Aspect());

        const int numAttachment = 1;
        fxcc::graph::common::RenderTargetJack::Desc desc(numAttachment, w, h, 4, 0);
        m_RenderTarget = std::make_shared<fxcc::graph::opengl3::RenderTargetJack>(desc);

    }
    void OnUpdate() override
    {
        opengl3::GlfwApp::OnUpdate();

        float ctTime = m_GlfwlTimer.m_CurrentTime;
        camera.FreeMove(m_Input, m_GlfwlTimer.m_DeltaTime, { 50.0f,50.0f });

        float deltaTime = m_GlfwlTimer.m_DeltaTime;

    }
};

int main()
{
    ztclog::pushFunction([](const ztclog::Line& line)
        {
            if (line.m_Type != ztclog::Line::Type::_line_debug_)
            {
                std::cout << line.m_Text << std::endl;
            } });

            using _App = SimpleApp;
            using _Desc = fxcc::platform::common::App::Desc;

            _Desc desc;
            desc.m_Pos = glm::ivec2(100, 100);
            desc.m_Size = glm::ivec2(900, 600);
            desc.m_ScaleFactor = 1.0f;

            desc.m_Title = "test font";
            std::shared_ptr<_App> _app = std::make_shared<_App>(desc);
            return _app->Run();
};
